import { getModule, getModules } from "./arcgisAPI";
import store from "./store";
import { getArcGISToken } from "./utils";

type searchType = "like" | "in" | ">" | "=" | "<" | "<>";

// token
const tokens: Map<string, string> = new Map();

export type SearchOption = {
  url: string;
  where?: { field: string; type: searchType; value: string | number }[];
  // 连接方式
  connection?: "and" | "or"[];
  geometry?: any;
  ids?: number[];
};

type StatisticsType = {
  onStatisticField: string;
  outStatisticFieldName?: string;
  statisticType: string;
};
export const query = async (option: SearchOption) => {
  const where = getWhere(option);
  const geometry = option.geometry || null;
  const modules = await getModules([
    "esri/tasks/QueryTask",
    "esri/tasks/support/Query"
  ]);
  const promise = new Promise<any>((resolve, reject) => {
    const [QueryTask, Query] = modules;
    const task = new QueryTask({ url: option.url });
    const query = new Query();
    query.returnGeometry = true;
    query.outFields = ["*"];
    query.where = where;
    query.geometry = geometry;
    query.outSpatialReference = store.spatialReference;
    if (option.ids && option.ids.length) {
      query.objectIds = option.ids;
    }
    task
      .execute(query)
      .then((res: any) => {
        resolve(res);
      })
      .catch((e: any) => {
        reject(e);
      });
  });
  return promise;
};

export const identify = async (option: SearchOption, view: any) => {
  const modules = await getModules([
    "esri/tasks/IdentifyTask",
    "esri/tasks/support/IdentifyParameters"
  ]);
  const promise = new Promise((resolve, reject) => {
    const [IdentifyTask, IdentifyParameters] = modules;
    let url = option.url;
    const params = new IdentifyParameters();
    params.returnGeometry = true;
    params.tolerance = 10;
    params.layerOption = "all";
    params.width = view.width;
    params.height = view.height;
    params.geometry = option.geometry;
    params.mapExtent = view.extent;
    // id
    const arr = option.url.split("/");
    const id = arr.pop() as string;
    if (!isNaN(parseInt(id))) {
      params.layerIds = [parseInt(id)];
      url = arr.join("/");
    }
    const task = new IdentifyTask({ url });
    task
      .execute(params)
      .then((res: any) => {
        resolve(res.results);
      })
      .catch((e: any) => {
        reject(e);
      });
  });
  return promise;
};

export const queryCount = async (option: SearchOption) => {
  const where = getWhere(option);
  const geometry = option.geometry || null;
  const modules = await getModules([
    "esri/tasks/QueryTask",
    "esri/tasks/support/Query"
  ]);
  const promise = new Promise<number>((resolve, reject) => {
    const [QueryTask, Query] = modules;
    const task = new QueryTask({ url: option.url });
    const query = new Query();
    query.where = where;
    query.geometry = geometry;
    query.outSpatialReference = store.spatialReference;
    task
      .executeForCount(query)
      .then((count: number) => {
        resolve(count);
      })
      .catch((e: any) => {
        reject(e);
      });
  });
  return promise;
};

export const queryIds = async (option: SearchOption) => {
  const modules = await getModules([
    "esri/tasks/QueryTask",
    "esri/tasks/support/Query"
  ]);

  const [QueryTask, Query] = modules;
  const promise = new Promise<number[]>((resolve, reject) => {
    const query = new Query();
    const task = new QueryTask({ url: option.url });
    const where = getWhere(option);
    const geometry = option.geometry || null;
    query.where = where;
    query.geometry = geometry;
    task
      .executeForIds(query)
      .then((ids: number[]) => {
        resolve(ids);
      })
      .catch((e: Error) => {
        reject(e);
      });
  });
  return promise;
};

export const queryStatistics = async (
  option: SearchOption,
  statistics: StatisticsType[],
  groupField: string
) => {
  const where = getWhere(option);
  const geometry = option.geometry || null;
  const modules = await getModules([
    "esri/tasks/QueryTask",
    "esri/tasks/support/Query"
  ]);
  const promise = new Promise<any>((resolve, reject) => {
    const [QueryTask, Query] = modules;
    const task = new QueryTask({ url: option.url });
    const query = new Query();
    query.where = where;
    query.geometry = geometry;
    query.outStatistics = statistics;
    query.groupByFieldsForStatistics = [groupField];
    task
      .execute(query)
      .then((res: any) => {
        resolve(res.features);
      })
      .catch((e: any) => {
        reject(e);
      });
  });
  return promise;
};

const getWhere = (option: SearchOption) => {
  const { where, connection } = option;
  let whereStr = "";
  if (where && where.length > 0) {
    const whereArr: string[] = [];
    where?.forEach(el => {
      const { field, type, value } = el;
      switch (type) {
        case "like":
          whereArr.push(`${field} like '%${value}%'`);
          break;
        case "in":
          whereArr.push(`${field} in (${value})`);
          break;
        case "<>":
          const val = typeof value === "string" ? `'${value}'` : value;
          whereArr.push(`${field} <> ${val}`);
          break;
        default:
          whereArr.push(`${field} ${type} ${value}`);
          break;
      }
    });
    whereStr = whereArr[0];
    for (let i = 1; i < whereArr.length; i++) {
      const con = connection && connection[i - 1] ? connection[i - 1] : "and";
      const str = whereArr[i];
      whereStr += con + " " + str;
    }
  } else {
    whereStr = "1=1";
  }
  return whereStr;
};

export const setToken = async (
  url: string,
  source: string,
  username: string,
  password: string
) => {
  const config = await getModule("esri/config");
  const promise = new Promise<void>((resolve, reject) => {
    const items = config.request.interceptors.filter((el: any) => {
      return el.urls === url;
    });
    if (!items.length) {
      setRequestToken(url, source, username, password)
        .then(() => {
          resolve();
        })
        .catch(e => {
          reject();
        });
    } else {
      resolve();
    }
  });
  return promise;
};

const setRequestToken = (
  url: string,
  source: string,
  username: string,
  password: string
) => {
  const promise = new Promise<void>((resolve, reject) => {
    const token = tokens.get(url);
    if (!token) {
      getToken(url, username, password)
        .then(token => {
          tokens.set(url, token);
          setEsriConfig(source, token);
          resolve();
        })
        .catch(e => {
          reject(e);
        });
    } else {
      setEsriConfig(url, token);
      resolve();
    }
  });
  return promise;
};

// 设置请求token
export const setEsriConfig = async (url: string, token: string) => {
  const config = await getModule("esri/config");
  if (config.request && config.request.interceptors) {
    let status = false;
    const interceptors = config.request.interceptors;
    interceptors.forEach((el: any) => {
      if (el.urls === url) {
        status = true;
        el.before = (params: any) => {
          if (url.includes("fwaq.zfj.xm.gov.cn")) {
            params.requestOptions.query["FWAQ-TOKEN"] = token;
            params.url = params.url + "?FWAQ-TOKEN=" + token;
          } else {
            params.requestOptions.query.token = token;
          }
        };
      }
    });
    if (status) {
      return;
    }
  }
  config.request.interceptors.push({
    urls: url,
    before: (params: any) => {
      if (url.includes("fwaq.zfj.xm.gov.cn")) {
        params.requestOptions.query["FWAQ-TOKEN"] = token;
      } else {
        params.requestOptions.query.token = token;
      }
    }
  });
};

// 获取token
const getToken = (url: string, username: string, password: string) => {
  const promise = new Promise<string>((resolve, reject) => {
    getArcGISToken(url, username, password)
      .then(token => {
        resolve(token);
      })
      .catch(e => {
        reject(e);
      });
  });
  return promise;
};

const parsingUrl = (url: string) => {
  const obj = new URL(url);
  return {
    protocol: obj.protocol,
    host: obj.hostname,
    port: obj.port,
    pathname: obj.pathname
  };
};
